package com.example.demo.listener;

import com.example.demo.mapper.*;
import com.example.demo.entity.*;
import com.example.demo.util.EmailUtil;
import com.example.demo.util.WarningEmailUtil;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.googlecode.aviator.runtime.type.AviatorObject;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.math3.linear.MatrixUtils;
import org.apache.commons.math3.linear.RealMatrix;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@Component
public class AutomaticWarningListener {

    private static final Integer MAX_SIZE =4000;
    private int passNum=0;
    private int inPassNum=0;
    private int level=0;
    private List<Integer> passsId=new ArrayList<>(MAX_SIZE);
    private List<Integer> inPassId=new ArrayList<>(MAX_SIZE);
    private Map<Integer,Integer> count=new HashMap<>(MAX_SIZE);
    private Map<Integer,Matrix> passStudent=new HashMap<>(MAX_SIZE);
    private Map<Integer,Matrix> inPassStudent=new HashMap<>(MAX_SIZE);

    private ReentrantLock reentrantLock = new ReentrantLock();

    private static final Expression expression = AviatorEvaluator.compile("a <= b? 1 : a <= c? 2 : 3");



    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private  StudentMapper sms;

    @Autowired
    private  ScoreMapper sm;

    @Autowired
    private  StudentMapper smm;

    @Autowired
    private  WarningInfoMapper wm;

    @Autowired
    private  CourseNumMapper cnm;

    @Autowired
    private  PassMapper pm;

    @Autowired
    private  CollegesMapper csm;

    @Autowired
    private  StuExternalMapper sem;

    @Autowired
    private  UserInfoMapper uim;

    @Autowired
    private   NeedMapper needMapper;

    @Resource(name = "sendEmail")
    private Executor emailExecutor;
    // 每5秒执行自动预警
    //@Scheduled(cron = "*/5 * * * * ?")
    @Async // 开启异步监听
    @EventListener(WorkApplicationEvent.class)
    public void performAutomaticWarning(WorkApplicationEvent workApplicationEvent) {
        // 在这里编写自动预警代码
        // 根据需要进行数据库查询、计算、判断等操作，并发送通知
        // 示例：假设通过成绩的矩阵为passMatrix，其他学生的矩阵为studentMatrix
        reentrantLock.lock();
        try {
            MyEvent myEvent = (MyEvent) workApplicationEvent.getSource();
            System.out.println(myEvent.getMessage());
            Map<Integer,Matrix> studentMatrixList = getAllStudentMatrix();
            getPassMatrix(studentMatrixList);
            for (Map.Entry<Integer, Matrix> entry : studentMatrixList.entrySet()) {
                int key = entry.getKey();
                Matrix studentMatrix = entry.getValue();
                if (!isSimilar(key,studentMatrix)) {
                    sendNotification(key,studentMatrix); // 发送预警通知
                }
            }
            updatePass(passsId,inPassId);
            clearResource();
        } finally {
            reentrantLock.unlock();
        }

    }

    private void clearResource() {
        passsId.clear();
        inPassId.clear();
        count.clear();
        passStudent.clear();
        inPassStudent.clear();
    }

    //得到现在与数据库里最新日期的相差天数
    public <T>long compareDate(List<T> list){
        Date date = new Date(System.currentTimeMillis());
        long time = date.getTime();
        long num=0;
        for(T obj:list){
            if (obj instanceof WarningInfos) {
                long time1 = ((WarningInfos)obj).getCreatetime().getTime();
                num+=time-time1;
            }else if(obj instanceof Pass){
                long time1=((Pass)obj).getCreatetime().getTime();
                num+=time-time1;
            }
            }
        long day=num/24/60/60/1000;
        if(num==0) day=70;
        return day;
        }

//拿到最新日期的学期
    public <T>String abtainLatest(int id,List<T> list){
        Date latestDate = null;
        for (T obj : list) {
            if (obj instanceof CourseNum) {
                Date currentDate = ((CourseNum) obj).getCreatetime();
                if (currentDate != null) {
                    if (latestDate == null || currentDate.after(latestDate)) {
                        latestDate = currentDate;
                    }
                }
            }
        }
        System.out.println(id);
        return cnm.selectCourseNumByCreateTimeAndId(latestDate,id).getTerm();
    }

//插入数据库，保留某学期是否通过
    public void updatePass(List<Integer> passidd, List<Integer> inpassidd) {
        int cc=0;
        int ccc=0;
        String term="";
        for (int i=0;i<passidd.size();i++){
            if(passidd.get(i)!=0) {
                int id=passidd.get(i);
                List<CourseNum> courseNums = cnm.selectCourseNumById(id);
                term=abtainLatest(id,courseNums);
                cc = Integer.valueOf(smm.selectStuById(id).getGrade().split("级")[0]);
                ccc=cc+1;
                String name=sms.selectStuById(passidd.get(i)).getName();
                Boolean ispass=true;
                List<Pass> passes = pm.selectPassById(id);
                long day=compareDate(passes);
               // if(day>60&&day<90) {
                    Pass p = new Pass(id, name, term, ispass,new Date(System.currentTimeMillis()));
                    pm.addPass(p);
                    System.out.println("success add");
               // }
            }
        }
        for (int i=0;i<inpassidd.size();i++){
            if(inpassidd.get(i)!=0) {
                int id=inpassidd.get(i);
                List<CourseNum> courseNums = cnm.selectCourseNumById(id);
                term=abtainLatest(id,courseNums);
                String name=sms.selectStuById(inpassidd.get(i)).getName();
                Boolean ispass=false;
                List<Pass> passes = pm.selectPassById(id);
                long day=compareDate(passes);
                if(day>60&&day<90) {
                    Pass p = new Pass(id, name, term, ispass,new Date(System.currentTimeMillis()));
                    pm.addPass(p);
                    System.out.println("success failure add");
                }
            }
        }
    }

    //判断矩阵是否相似(利用欧几里得距离定义相似度)
    public boolean isSimilar(int id,Matrix other) {
        double sim = 0;
        double[][] otherScore = other.getPass();
        if(otherScore.length!=level) return false;
        if(other.getPass().length!=8) {
                if(level!=8) {
                    for (Matrix passmatrixs : passStudent.values()) {
                        for (int i = 0; i < passmatrixs.getPass()[level - 1].length; i++) {
                            sim += (Math.sqrt(passmatrixs.getPass()[level - 1][i] - otherScore[level - 1][i]));
                        }
                    }
//                    sim += 1 / sim;
                    sim = sim / passsId.size();
                    if (sim >= 0.5) {
                        boolean b = passsId.stream()
                                .anyMatch(item -> item == id);
                        if (!b) {
                            passStudent.put(id, other);
                            passsId.add(passNum++, id);
                        }
                        return true;
                    } else {
                        sim = 0;
                        boolean b = inPassId.stream()
                                .anyMatch(item -> item == id);
                        if (!b) {
                            inPassStudent.put(id, other);
                            inPassId.add(inPassNum++, id);
                        }
                    }
                    return false;
                }else{
                    RealMatrix realMatrixother = MatrixUtils.createRealMatrix(otherScore);
                    int i=0;
                    for(int b:passStudent.keySet()){
                        RealMatrix realMatrix = MatrixUtils.createRealMatrix(passStudent.get(b).getPass());
                        RealMatrix covarianceMatrix = realMatrix.transpose().multiply(realMatrixother.transpose());
                        double rvCoefficient = covarianceMatrix.getTrace() /
                                (Math.sqrt(realMatrix.transpose().multiply(realMatrix).getTrace()) *
                                        Math.sqrt(realMatrixother.transpose().multiply(realMatrixother).getTrace()));

                        sim += rvCoefficient;
                    }
                    sim = sim / passsId.size();
                    if (sim >= 0.5){
                        return true;
                    }else return false;
                }
        }else {
            RealMatrix realMatrixother = MatrixUtils.createRealMatrix(otherScore);
            int i = 0;
            for (int b : passStudent.keySet()) {
                if (passStudent.get(b).getPass().length == otherScore.length){
                    RealMatrix realMatrix = MatrixUtils.createRealMatrix(passStudent.get(b).getPass());
                RealMatrix covarianceMatrix = realMatrix.transpose().multiply(realMatrixother.transpose());//求协方差
                double rvCoefficient = covarianceMatrix.getTrace() /
                        (Math.sqrt(realMatrix.transpose().multiply(realMatrix).getTrace()) *
                                Math.sqrt(realMatrixother.transpose().multiply(realMatrixother).getTrace()));

                sim += rvCoefficient;
                sim = sim / passsId.size();
                if (sim >= 0.5) {
                    return true;
                } else return false;
            }
        }
        }
        return false;
    }

    // 获取通过成绩的矩阵
    private Map<Integer,Matrix> getPassMatrix(Map<Integer,Matrix> map) {
        double grade=0;
        double xuefen=0;
        double atainxuefen=0;
        List<Student> stuList=smm.findAlllStu();
        for(Student student:stuList) {
            List<CourseNum> courseNumsss = cnm.selectCourseNumById(student.getId());
            String stuterm = abtainLatest(student.getId(), courseNumsss);
            String[] ss = sms.selectStuById(student.getId()).getGrade().split("级");
            int grades = Integer.valueOf(ss[0]);
            String[] split = stuterm.split("-");
            int year = Integer.valueOf(split[0]);
            int sss = year - grades;
            if(sss>3){
                continue;
            }
            List<Score> vertfy=sm.findScoreByOne(student.getId());
            if(vertfy.size()!=0) {
                Matrix matrix = map.get(student.getId());
                if (matrix != null){
                    int length = matrix.getPass().length;
                Float s = csm.selectByCollegeAndVersion(smm.selectStuById(student.getId()).getCollege(), student.getGrade().split("级")[0]).getGraduationGredit();
                    String gredit111 = redisTemplate.opsForValue().get("gredit");
                    if(gredit111!=null&&!"".equals(gredit111)){
                        s=Float.valueOf(gredit111);
                    }
                    if (length == 1) {
                    level = 1;
                    List<Score> scoreList = sm.findScoreByOne(student.getId());
                    if (scoreList.size() != 0) {
                        for (Score score : scoreList) {
                            List<Need> needs = needMapper.selectByCollegeAndTerm(score.getMajor(), score.getTerm());
                            List<Need> needList = needs.stream().filter(need -> need.getVersion() == Integer.valueOf(student.getGrade().
                                    split("级")[0])).collect(Collectors.toList());
                            for(Need need:needList) {//判断是否需要计算绩点
                                if (need.getNeedCourse().contains(score.getCourse())) {
                                    if(need.getType()==1) {
                                        if (score.getFscore() >= 60) {
                                            atainxuefen += score.getGredit();
                                        } else atainxuefen += 0;
                                        grade = grade + ((double) (score.getFscore() / 10) - 5) * score.getGredit();
                                        xuefen = xuefen + score.getGredit();
                                    }else{
                                        if (score.getFscore() >= 60) {
                                            atainxuefen += score.getGredit();//选修也是算学分的
                                        } else atainxuefen += 0;
                                    }
                                    break;
                                }
                            }
                        }
                        grade = grade / xuefen;
                        List<CourseNum> courseNums = cnm.selectCourseNumById(student.getId());
                        String term = abtainLatest(student.getId(), courseNums);
                        addStuEXternal(grade, atainxuefen, student, matrix, s, term);
                        grade = 0;
                        xuefen = 0;
                        atainxuefen = 0;
                    }
                } else {
                    level = length;
                    if (level != 8) {
                        List<CourseNum> courseNums = cnm.selectCourseNumById(student.getId());
                        String terms = abtainLatest(student.getId(), courseNums);
                        List<Score> scoreList = sm.findScoreByTermAndId(student.getId(), terms);
                        if (scoreList.size() != 0) {
                            for (Score score : scoreList) {
                                List<Need> needs = needMapper.selectByCollegeAndTerm(score.getMajor(), score.getTerm());
                                List<Need> needList = needs.stream().filter(need -> need.getVersion() == Integer.valueOf(student.getGrade().
                                        split("级")[0])).collect(Collectors.toList());
                                for(Need need:needList) {//判断是否需要计算绩点
                                    if(need.getNeedCourse().contains(score.getCourse())) {
                                        if(need.getType()==1) {
                                            if (score.getFscore() >= 60) {
                                                atainxuefen += score.getGredit();
                                            } else atainxuefen += 0;
                                            grade = grade + ((double) (score.getFscore() / 10) - 5) * score.getGredit();
                                            xuefen = xuefen + score.getGredit();
                                        }else{
                                            if (score.getFscore() >= 60) {
                                                atainxuefen += score.getGredit();
                                            } else atainxuefen += 0;
                                        }
                                        break;
                                        }
                                }
                            }
                            grade = grade / xuefen;
                            List<CourseNum> courseNumss = cnm.selectCourseNumById(student.getId());
                            String term = abtainLatest(student.getId(), courseNumss);
                            addStuEXternal(grade, atainxuefen, student, matrix, s, term);
                        }
                    } else {
                        double total = 0;
                        double totals = 0;
                        double averytotal = 0;
                        double xuefen1 = 0;
                        List<CourseNum> courseNums = cnm.selectCourseNumById(student.getId());
                        String onlyneedterm = abtainLatest(student.getId(), courseNums);
                        List<Score> scoreByTermAndId = sm.findScoreByTermAndId(student.getId(), onlyneedterm);
                        if (scoreByTermAndId.size() != 0) {
                            for (Score z : scoreByTermAndId) {
                                List<Need> needs = needMapper.selectByCollegeAndTerm(z.getMajor(), z.getTerm());
                                List<Need> needList = needs.stream().filter(need -> Objects.equals(need.getVersion(), Integer.valueOf(student.getGrade().
                                        split("级")[0]))).collect(Collectors.toList());
                                for(Need need:needList) {//判断是否需要计算绩点
                                    if (need.getNeedCourse().contains(z.getCourse())) {
                                        if(need.getType()==1) {
                                            total += ((double) (z.getFscore() / 10) - 5) * z.getGredit();
                                            xuefen1 += z.getGredit();
                                            if (z.getFscore() >= 60)
                                                totals += z.getGredit();//计算拿到的学分
                                            else totals += 0;
                                        }else{
                                            if (z.getFscore() >= 60)
                                                totals += z.getGredit();//计算拿到的学分
                                            else totals += 0;
                                        }
                                        break;
                                    }
                                }
                            }
                            total = total / xuefen1;
                            List<StuExternal> stuExternals = sem.selectById(student.getId());
                            for (StuExternal stuExternal1 : stuExternals) {
                                if (!"end".equals(stuExternal1.getTerm())) {
                                    total += stuExternal1.getGredit();
                                    totals += stuExternal1.getCredit();
                                }
                            }
                            averytotal = total / level;
                            if (averytotal >= csm.selectByCollegeAndVersion(student.getCollege(), student.getGrade().split("级")[0]).getGraduationGredit()) {
                                if (!passStudent.containsKey(student.getId())) {
                                    passStudent.put(student.getId(), matrix);
                                    passsId.add(passNum++, student.getId());
                                }
                            } else if (!inPassStudent.containsKey(student.getId())) {
                                inPassStudent.put(student.getId(), matrix);
                                inPassId.add(inPassNum++, student.getId());
                            }
                            StuExternal stuend = new StuExternal(student.getId(), student.getName(), student.getCollege(), averytotal, "end", 0, "无", totals);
                            StuExternal stuExternal = sem.selectByIdTerm(student.getId(), "end");
                            int add1 = 0;
                            if (stuExternal == null) {
                                add1 = sem.add(stuend);
                                if (add1 > 0) {
                                    System.out.println("end success add");
                                }
                            }
                        }
                    }
                    grade = 0;
                    xuefen = 0;
                    atainxuefen = 0;
                }
            }
            }
        }
        if(inPassNum==0){
            System.out.println("all pass");
        }
        return passStudent;
    }

    private void addStuEXternal(double grade, double atainxuefen, Student student, Matrix matrix, Float s, String term) {
        StuExternal stuExternal = new StuExternal(student.getId(), student.getName(), student.getCollege(), grade, term, 0, "无", atainxuefen);
        int add = 0;
        if (sem.selectByIdTerm(student.getId(), term) == null) {
            add = sem.add(stuExternal);
            if (add > 0) System.out.println("插入成功");
        }
        if (grade >= s) {
            if (!passStudent.containsKey(student.getId())) {
                passStudent.put(student.getId(), matrix);
                passsId.add(passNum++, student.getId());
            }
            System.out.println("通过id=" + student.getId());
        } else if (grade < s) {
            if (!inPassStudent.containsKey(student.getId())) {
                inPassStudent.put(student.getId(), matrix);
                inPassId.add(inPassNum++, student.getId());
            }
        }
    }

    public double[][] atainendMa(int row,double[][] matrix,List<Score> list){
        int flags=0;
        for (Score score : list) {
            if (score.getFscore() >=60) {
                matrix[row][flags++] = 1;
            } else matrix[row][flags++] = 0;
        }
                return matrix;
    }

    // 获取所有学生的成绩矩阵
    private Map<Integer,Matrix> getAllStudentMatrix() {
        int grade=0;
        int year=0;
        int term=0;
        Map<Integer, Matrix> studentMatrices = new HashMap<>();
        List<Student> stuList=smm.findAlllStu();
        for(Student student:stuList){
            List<Score> verify1=sm.findScoreByOne(student.getId());
            if(verify1.size()!=0) {
                List<CourseNum> courseNums = cnm.selectCourseNumById(student.getId());
                String stuterm = abtainLatest(student.getId(), courseNums);
                List<Score> scoreList = sm.findScoreByTermAndId(student.getId(), stuterm);
                if (scoreList.size() != 0){
                    String[] ss = sms.selectStuById(student.getId()).getGrade().split("级");
                grade = Integer.valueOf(ss[0]);
                String[] split = stuterm.split("-");
                year = Integer.valueOf(split[0]);
                term = Integer.valueOf(split[2]);
                int s = year - grade;
                if (term == 1) {
                    double[][] ma = new double[s * 2 + 1][scoreList.size() + 1];
                    ma = atainendMa(s * 2, ma, scoreList);
                    Matrix matrix = new Matrix(ma);
                    studentMatrices.put(student.getId(), matrix);
                } else {
                    if (s < 3) {
                        double[][] ma = new double[s * 2 + 2][scoreList.size() + 1];
                        ma = atainendMa(s * 2 + 1, ma, scoreList);
                        Matrix matrix = new Matrix(ma);
                        studentMatrices.put(student.getId(), matrix);
                    } else {
                        if(s>3){
                            continue;
                        }
                        double[][] ma = new double[s * 2 + 2][scoreList.size() + 1];
                        ma = atainendMa(s * 2 + 1, ma, scoreList);
                        //2024-2025-2
                        int yrar = year + 1;
                        String searchterm = year + "-" + yrar + "-1";
                        List<Score> scoreByTermAndId = sm.findScoreByTermAndId(student.getId(), searchterm);
                        ma = atainendMa(s * 2, ma, scoreByTermAndId);
                        int num = 1;
                        for (int i = 1; i <= 3; i++) {
                            int before = year - 1;
                            int after = before + 1;
                            for (int j = 1; j <= 2; j++) {
                                int end = 0;
                                if (j == 1) end = 2;
                                else end = 1;
                                searchterm = before + "-" + after + "-" + end;
                                scoreByTermAndId = sm.findScoreByTermAndId(student.getId(), searchterm);
                                ma = atainendMa(s * 2 - num, ma, scoreByTermAndId);
                                num++;
                            }
                        }
                        Matrix matrix = new Matrix(ma);
                        studentMatrices.put(student.getId(), matrix);
                    }
                }
            }
            }
        }
        return studentMatrices;
    }

    // 发送预警通知
    private void sendNotification(int id,Matrix studentMatrix) {
        Map<String, Object> variables = new HashMap<>();
        int level1 = 1;
        int level2 = 3;
        String method = "系统信息";
        String lowlevel = redisTemplate.opsForValue().get("lowlevel");
        String middellevel = redisTemplate.opsForValue().get("middellevel");
        String methodway = redisTemplate.opsForValue().get("method");
        if (methodway != null && !"".equals(methodway)) {
            method = methodway;
        }
        if (lowlevel != null && !"".equals(lowlevel)) {
            level1 = Integer.parseInt(lowlevel);
        }
        if (middellevel != null && !"".equals(middellevel)) {
            level2 = Integer.parseInt(middellevel);
        }
        String sterm = abtainLatest(id, cnm.selectCourseNumById(id));
        List<WarningInfos> selectbyid = wm.selectbyid(id);
        double[][] pass = studentMatrix.getPass();
        int row = pass.length - 1;
        int num = 0;
        for (int i = 0; i < pass[row].length; i++) {
            if (pass[row][i] == 0) {
                num++;
            }
        }
        int number = selectbyid.size();
        variables.put("a", number);
        variables.put("b", level1);
        variables.put("c", level2);
        Long execute = (Long)expression.execute(variables);
        int  result = execute.intValue();
        String detail = "";
        Random r = new Random();
        int random = r.nextInt(1500000) + 100000000;
        if (method.equals("系统信息")){
            if (count.get(id) == null || row != count.get(id)) {
                count.put(id, row);
                // 实现发送预警通知的逻辑
                Student student = smm.selectStuById(id);
                Date date = new Date(System.currentTimeMillis());
                detail = getDetail(num, result, detail);
                WarningInfos wmm = new WarningInfos(random, student.getName(), id, student.getMajor(), student.getClassi(), result, detail, sterm, date, date, 0, 1);
                int n = wm.addWarningInfo(wmm);
                if (n > 0) System.out.println("success warning");
                else System.out.println("false warning！");
            }
    }else{
            WarningEmailUtil warn=new WarningEmailUtil();
            // 实现发送预警通知的逻辑
            Student student = smm.selectStuById(id);
            Date date = new Date(System.currentTimeMillis());
            detail = getDetail(num, result, detail);
            WarningInfos  wmm = new WarningInfos(random, student.getName(), id, student.getMajor(), student.getClassi(), result, detail, sterm, date, date, 0, 1);
            String detailV2 = detail;
            EmailUtil.sendEmailAsyncWithRetry(() -> warn.sendEmail("3121289785@qq.com",uim.selectByName(student.getName()).getEmail(),detailV2),
                    3, emailExecutor);

            int n = wm.addWarningInfo(wmm);
            if (n > 0) System.out.println("success warning");
            else System.out.println("false warning！");
        }
    }

    private static String getDetail(int num, int result, String detail) {
        switch (result) {
            case 1:
                detail = "该同学这学期挂科了" + num + "门";
                break;
            case 2:
                detail = "该同学这学期挂科了" + num + "门" + ",之前已经被预警过2-3次，预警级别升级为中度";
                break;
            case 3:
                detail = "该同学这学期挂科了" + num + "门" + ",之前已经被预警过超过3次，预警级别升级严重";
                break;
        }
        return detail;
    }
}